package com.sk.bilibili.apiMapper.component;


import com.sk.bilibili.apiMapper.annotation.*;
import com.sk.bilibili.apiMapper.bean.Args;
import com.sk.bilibili.apiMapper.bean.RequestInfo;
import com.squareup.okhttp.RequestBody;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 邵康
 * @date 2021/7/10 21:48
 */
public class ApiMapperProxy implements InvocationHandler {

    private RequestHandler requestHandler;
    private static final Class IGNORE_CLASS=Object.class;
    private Map<Method, RequestInfo> requestMap;

    public ApiMapperProxy(Class mapperClass, RequestHandler requestHandler) {
        this.requestHandler = requestHandler;
        init(mapperClass);
    }

    private void init(Class<?> mapperClass){
        requestMap=new HashMap<>();
        String baseUrl="";
        String baseMethod="";
        List<String> baseRemoveHeaders=new ArrayList<>();
        Map<String,String> baseAddHeaders=new HashMap<>();
        ApiMapper apiMapper = mapperClass.getAnnotation(ApiMapper.class);
        if(apiMapper!=null){
            baseUrl=apiMapper.value();
            baseMethod=apiMapper.method();
        }
        RequestHeads requestHeads = mapperClass.getAnnotation(RequestHeads.class);
        RequestHead requestHead = mapperClass.getAnnotation(RequestHead.class);
        handleRequestHeader(requestHead, requestHeads,baseAddHeaders,baseRemoveHeaders);
        Method[] methods = mapperClass.getDeclaredMethods();
        for (Method method : methods) {
            handleMethod(method,baseUrl,baseMethod,baseAddHeaders,baseRemoveHeaders);
        }
    }

    private void handleRequestHeader(RequestHead requestHead, RequestHeads requestHeads, Map<String, String> addHeaders,
                                     List<String> removeHeaders) {
        if(requestHeads!=null){
            RequestHead[] heads = requestHeads.value();
            for (RequestHead head : heads) {
                if(head.remove()){
                    removeHeaders.add(head.key());
                }else {
                    addHeaders.put(head.key(),head.value());
                }
            }
        }
        if(requestHead!=null){
            if(requestHead.remove()){
                removeHeaders.add(requestHead.key());
            }else {
                addHeaders.put(requestHead.key(),requestHead.value());
            }
        }
    }

    private void handleMethod(Method method, String baseUrl, String baseMethod,
                              Map<String, String> baseAddHeaders, List<String> baseRemoveHeaders) {
        RequestInfo request = new RequestInfo();
        request.addAddHeader(baseAddHeaders);
        request.addRemoveHeader(baseRemoveHeaders);
        ApiMapper apiMapper = method.getAnnotation(ApiMapper.class);
        if(apiMapper!=null){
            baseUrl+=apiMapper.value();
            if(!apiMapper.method().equals(""))baseMethod=apiMapper.method();
        }
        request.setUrl(baseUrl);
        request.setMethod(baseMethod);
        RequestHeads requestHeads = method.getAnnotation(RequestHeads.class);
        RequestHead requestHead = method.getAnnotation(RequestHead.class);
        handleRequestHeader(requestHead,requestHeads,request.getAddHeaders(),request.getRemoveHeaders());
        request.setReturnClass(method.getReturnType());
        FieldPath fieldPath = method.getAnnotation(FieldPath.class);
        if(fieldPath!=null)request.setPath(fieldPath.value());
        request.check();
        requestMap.put(method,request);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getDeclaringClass().equals(IGNORE_CLASS)){
            return method.invoke(this,args);//对于  Object的方法 自己处理
        }else {
            RequestInfo request = requestMap.get(method);
            if(request==null)throw new RuntimeException("方法:"+method+"没有指定API信息!");
            Args res=parseArgs(method,args);
            return requestHandler.handleRequest(request,res);
        }
    }

    private Args parseArgs(Method method, Object[] args) {
        Args res = new Args();
        if(args==null)return res;
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < args.length; i++) {
            if(args[i] instanceof RequestBody){
                res.setRequestBody((RequestBody) args[i]);
            }else {
                String name = parameters[i].getName();
                if(parameters[i].isAnnotationPresent(Param.class)){
                    name=parameters[i].getAnnotation(Param.class).value();
                }
                res.put(name,args[i]);
            }
        }
        return res;
    }

}






















